package com.android.launcher3.model;

import android.content.Context;
import android.content.Intent;
import android.provider.Settings;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.config.SPConfig;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.IntMap;
import com.android.launcher3.util.Logger;
import com.android.launcher3.util.SpManager;
import com.google.gson.reflect.TypeToken;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class LauncherApp {


    public enum App {
        NAVIGATION("导航", R.drawable.selector_icon_bg_lit_navi, R.mipmap.icon_big_navigation),
        PHONE("电话", R.drawable.selector_icon_bg_lit_phone, R.mipmap.icon_big_phone),
        MUSIC("音乐", R.drawable.selector_icon_bg_lit_music, R.mipmap.icon_big_music),
        TUNER("收音机", R.drawable.selector_icon_bg_lit_tuner, R.mipmap.icon_big_tuner),
        SETTINGS("设置", R.drawable.selector_icon_bg_lit_setting, R.mipmap.icon_big_setting),
        AIR_CONDITIONER("空调", R.drawable.selector_icon_bg_lit_airconditioner, R.mipmap.icon_big_airconditioner),
        PANORAMA("360全景", R.drawable.selector_icon_bg_lit_panorama, R.mipmap.icon_big_panorama),
        CAR_MEDIA("Media", R.drawable.ic_launcher_home, R.drawable.ic_launcher_home),
        ;
        public final String title;
        @DrawableRes
        public final int icon;
        public final int background;


        //    LauncherFakeApp(String title, @DrawableRes int res) {
//        this(title, res, res);
//    }
        App(String title, @DrawableRes int icon, @DrawableRes int bg) {
            this.title = title;
            this.icon = icon;
            this.background = bg;
        }

        @NonNull
        public Intent getIntent() {
            Intent intent = new Intent();
            //TODO 启动筛选
            intent.setAction(Intent.ACTION_MAIN);
            intent.addCategory(Intent.CATEGORY_DEFAULT);
//        intent.addCategory(Intent.CATEGORY_HOME);
//        intent.addCategory(getClass().getName()+"."+name());
            switch (this) {
                case NAVIGATION:
                    intent.setPackage("com.google.android.apps.maps");
                    break;
                case PHONE:
                    intent.setPackage("com.android.dialer");
                    break;
                case MUSIC:
                    intent.setPackage("com.google.android.music");
                    break;
                case TUNER:
                    break;
                case SETTINGS:
                    intent.setAction(Settings.ACTION_SETTINGS);
                    break;
                case AIR_CONDITIONER:
                    break;
                case PANORAMA:
                    break;
                case CAR_MEDIA:
                    intent.setPackage("com.android.car.media");
                    break;
            }
            return intent;
        }
    }

    public final App APP;
    private int screenId;
    private int cellX;
    private int cellY;

    private LauncherApp(App app) {
        APP = app;
    }

    private static final boolean AUTO_ALIGN = false;

    private static void save(Context context) {
        save(context, shortcutInfos);
    }

    private static void save(Context context, List<WorkspaceItemInfo> list) {
        List<LauncherApp> apps = null;
        if (list != null && list.size() > 0) {
            apps = new ArrayList<>();
            for (int i = 0; i < list.size(); i++) {
                WorkspaceItemInfo info = list.get(i);
                if (info == null) continue;
                LauncherApp la = new LauncherApp(info.app);
                la.screenId = info.screenId;
                la.cellX = info.cellX;
                la.cellY = info.cellY;
                apps.add(la);
            }
        }
        SpManager.putBean(context.getApplicationContext(),
                SPConfig.SP_NAME_PROPERTY, SPConfig.KEY_LAUNCHER_APP_LIST, apps);
    }

    @Nullable
    private static List<WorkspaceItemInfo> load(Context context) {
        List<WorkspaceItemInfo> list = null;
        List<LauncherApp> containFApp = null;
        try {
            containFApp = SpManager.getBean(context,
                    SPConfig.SP_NAME_PROPERTY, SPConfig.KEY_LAUNCHER_APP_LIST, null,
                    new TypeToken<List<LauncherApp>>() {
                    }.getType());
            if (containFApp != null) {
                list = new ArrayList<>();
                for (int i = 0; i < containFApp.size(); i++) {
                    try {
                        LauncherApp app = containFApp.get(i);
                        list.add(createLauncherApp(app));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (containFApp != null) {
            if (list == null) {
                list = new ArrayList<>();
            }
            int count = list.size();
            LauncherApp.App[] values = LauncherApp.App.values();
            boolean contain;
            for (int i = values.length - 1; i >= 0; i--) {
                contain = false;
                for (int j = 0; j < containFApp.size(); j++) {
                    LauncherApp launcherApp = containFApp.get(j);
                    if (launcherApp == null) continue;
                    if (launcherApp.APP == values[i]) {
                        contain = true;
                        break;
                    }
                }
                if (!contain) {
                    list.add(createLauncherApp(values[i], count));
                    count++;
                }
            }
        }
        Logger.d(LauncherApp.class, "LauncherApp load :");
        logList(list);
        Logger.d(LauncherApp.class, "LauncherApp load END");
        return list;
    }

    private static void sortNVanAppList(List<WorkspaceItemInfo> list) {
        if (list == null || list.size() == 0) {
            return;
        }
        Params[] params = {
                new Params(Workspace.FIRST_SCREEN_ID_NVAN, 4, 1, true),
                new Params(Workspace.FIRST_SCREEN_ID_NVAN + 1, 5, 2, false)
        };
        SortScreenApp sort = new SortScreenApp(params);
        sort.sort(list);

    }

    @Nullable
    private static List<WorkspaceItemInfo> shortcutInfos;

    @NonNull
    public static List<WorkspaceItemInfo> getAppList(Context context) {
        List<WorkspaceItemInfo> list = LauncherApp.shortcutInfos;
        if (list == null || list.size() == 0) {
            list = load(context);
            if (list == null || list.size() == 0) {
                list = getVManShortcutList(App.values());
            }
            sortNVanAppList(list);
            LauncherApp.shortcutInfos = list;
        }
        return list;
    }

    @NonNull
    public static List<Integer> getScreens(Context context) {
        List<WorkspaceItemInfo> appList = getAppList(context);
        final List<Integer> screens = new ArrayList<>();
        for (WorkspaceItemInfo info : appList) {
            if (!screens.contains(info.screenId)) {
                screens.add(info.screenId);
            }
        }
        return screens;
    }

    @NonNull
    private static List<WorkspaceItemInfo> getVManShortcutList(@NonNull LauncherApp.App[] arr) {
        final List<WorkspaceItemInfo> shortcutInfos = new ArrayList<>();
        for (int i = 0; i < arr.length; i++) {
            LauncherApp.App app = arr[i];
            WorkspaceItemInfo info = createLauncherApp(app, i);

            shortcutInfos.add(info);
        }
        return shortcutInfos;
    }

    private static WorkspaceItemInfo createLauncherApp(@NonNull LauncherApp app) {
        WorkspaceItemInfo info = new WorkspaceItemInfo(app.APP);
        info.screenId = app.screenId;
        info.cellX = app.cellX;
        info.cellY = app.cellY;
        return info;
    }

    private static WorkspaceItemInfo createLauncherApp(@NonNull LauncherApp.App app, int position) {
        WorkspaceItemInfo info = new WorkspaceItemInfo(app);
        info.setPosition(position);
        return info;
    }

    public static void updateItemInfoProps(Context context, ItemInfo item,
                                           int container, int screenId,
                                           int cellX, int cellY) {
        Logger.d(LauncherApp.class, "LauncherApp updateItemInfoProps :" +
                "\nitem:" + item.title +
                "\ncontainer:" + container +
                "\nscreenId:" + screenId +
                "\ncellX:" + cellX +
                "\ncellY:" + cellY
        );
        if (!(item instanceof WorkspaceItemInfo)) {
            return;
        }
        WorkspaceItemInfo shortcutInfo = (WorkspaceItemInfo) item;
//        if (shortcutInfo.container == container &&
//                shortcutInfo.screenId == screenId &&
//                shortcutInfo.cellX == cellX &&
//                shortcutInfo.cellY == cellY) {
//            return;
//        }
        List<WorkspaceItemInfo> infoList = LauncherApp.shortcutInfos;
        if (infoList == null) {
            return;
        }
        boolean temp = false;
        for (int i = 0; i < infoList.size(); i++) {
            WorkspaceItemInfo info = infoList.get(i);
            if (info.app == shortcutInfo.app) {
                infoList.remove(i);
                temp = true;
                shortcutInfo = info;
                shortcutInfo.container = container;
                shortcutInfo.screenId = screenId;
                shortcutInfo.cellX = cellX;
                shortcutInfo.cellY = cellY;
                break;
            }
        }
        if (!temp) {
            return;
        }

        boolean init = false;
        for (int i = 0; i < infoList.size(); i++) {
            WorkspaceItemInfo info = infoList.get(i);
            if (init) {
//                info.setPosition(i);
            } else {
                if (info.screenId < shortcutInfo.screenId) {
                    continue;
                } else if (info.screenId == shortcutInfo.screenId) {
                    if (info.cellX >= cellX && info.cellY >= cellY) {
                        infoList.add(i, shortcutInfo);
//                        shortcutInfo.setPosition(i);
                        init = true;
                    }
                } else {
                    infoList.add(i, shortcutInfo);
//                    shortcutInfo.setPosition(i);
                    init = true;
                }

            }
        }
        if (!init) {
            int size = infoList.size();
            infoList.add(size, shortcutInfo);
//            shortcutInfo.setPosition(size);
        }
        save(context, infoList);
        //
        logList(infoList);
        Logger.d(LauncherApp.class, "LauncherApp updateItemInfoProps END");
    }

    private static void logList(List<WorkspaceItemInfo> list) {
        if (list == null || list.size() == 0) {
            return;
        }
        for (WorkspaceItemInfo info : list) {
            Logger.d(LauncherApp.class, "title=" + (info.app == null ? null : info.app.title) +
                    " , screenId=" + info.screenId +
                    " , cellX=" + info.cellX +
                    " , cellY=" + info.cellY);
        }

    }

    private static class SortScreenApp {
        /**
         * {{cellX,cellY}}
         */
        private final Params[] RULES;
        private final IntMap<Params> MAP;

        public SortScreenApp(@NonNull Params... p) {
            this.RULES = p;
            this.MAP = new IntMap<>();
        }

        public void sort(List<WorkspaceItemInfo> list) {
            if (list == null || list.size() == 0) {
                return;
            }
            if (RULES.length == 0) {
                throw new RuntimeException("SortScreenApp params.length == 0");
            }
            MAP.clear();
            for (int i = 0; i < RULES.length; i++) {
                MAP.put(RULES[i].screenId, new Params(RULES[i]));
            }
            for (int i = 0; i < list.size(); i++) {
                WorkspaceItemInfo itemInfo = list.get(i);
                sort(itemInfo);
            }
            list.clear();
            Iterator<Integer> iterator = MAP.keySet().iterator();
            while (iterator.hasNext()) {
                Integer key = iterator.next();
                Params params = MAP.get(key);
                if (params != null) {
                    for (int i = 0; i < params.items.length; i++) {
                        WorkspaceItemInfo[] item = params.items[i];
                        for (int j = 0; j < item.length; j++) {
                            if (item[j] == null) continue;
                            list.add(item[j]);
                        }
                    }
                }
            }
        }

        private void sort(WorkspaceItemInfo info) {
            if (info == null) {
                return;
            }
            boolean init = false;
            Params params;
            //try to fill in exit screen
            for (Integer key : MAP.keySet()) {
                params = MAP.get(key);
                if (params == null) continue;
                if (!params.fill) continue;
                if (params.isFulled()) continue;
                init = params.init(info);
                if (init) {
                    return;
                }
            }

            Integer maxKey = MAP.getMaxKey();
            int newScreenId;
            if (maxKey == null) {
                newScreenId = Workspace.FIRST_SCREEN_ID_NVAN;
            } else {
                newScreenId = maxKey + 1;
            }
            params = MAP.get(info.screenId);
            if (params != null) {
                //add in exit screen
                init = params.init(info);
            }

            if (!init) {
                //add in new screen
                Params rule = RULES[RULES.length - 1];
                Params newParams = new Params(newScreenId, rule.cellX, rule.cellY, rule.fill);
                MAP.put(newParams.screenId, newParams);
                info.screenId = newParams.screenId;
                init = newParams.init(info);
            }

            if (!init) {
                TestProtocol.e(getClass().getName(), "LauncherApp sort init failed : " +
                        "title = " + info.title +
                        "screenId = " + info.screenId +
                        "cellX = " + info.cellX +
                        "cellY = " + info.cellY
                );
            }
        }
//        private void sort(WorkspaceItemInfo info) {
//            if (info == null) {
//                return;
//            }
//            Params params = MAP.get(info.screenId);
//            Integer maxKey = MAP.getMaxKey();
//            int newScreenId;
//            if (maxKey == null) {
//                newScreenId = Workspace.FIRST_SCREEN_ID_NVAN;
//            } else {
//                newScreenId = maxKey + 1;
//            }
//            if (params == null) {
//                Params rule = RULES[RULES.length - 1];
//                Params newParams = new Params(newScreenId, rule.cellX, rule.cellY, rule.full);
//                MAP.put(newParams.screenId, newParams);
//                info.screenId = newParams.screenId;
//                sort(info);
//                return;
//            }
//            boolean init = params.init(info);
//            if (!init) {
//                info.screenId = newScreenId;
//                sort(info);
//            }
//        }
    }

    private static class Params {
        public final WorkspaceItemInfo[][] items;
        public final int screenId, cellX, cellY;
        public final boolean fill;

        public Params(int screenId, int cellX, int cellY) {
            this(screenId, cellX, cellY, true);
        }

        public Params(@NonNull Params p) {
            this(p.screenId, p.cellX, p.cellY, p.fill);
        }

        public Params(int screenId, int cellX, int cellY, boolean f) {
            this.items = new WorkspaceItemInfo[cellX][cellY];
            this.screenId = screenId;
            this.cellX = cellX;
            this.cellY = cellY;
            this.fill = f;
        }

        public boolean init(@NonNull WorkspaceItemInfo info) {
            int x = info.cellX;
            int y = info.cellY;

            if (x >= cellX) {
                x = cellX - 1;
            }
            if (y >= cellY) {
                y = cellY - 1;
            }

            try {
                if (items[x][y] == null) {
                    items[x][y] = info;
                    info.cellX = x;
                    info.cellY = y;
                    info.screenId = screenId;
                    return true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (!fill) {
                return false;
            }
            if (isFulled()) {
                return false;
            }
            //try full
            x = -1;
            y = -1;
            for (int i = 0; i < items.length; i++) {
                WorkspaceItemInfo[] item = items[i];
                if (item == null) {
                    item = new WorkspaceItemInfo[cellY];
                    items[i] = item;
                    x = i;
                    y = 0;
                    break;
                }
                for (int j = 0; j < item.length; j++) {
                    if (item[j] == null) {
                        x = i;
                        y = j;
                        break;
                    }
                }
                if (x >= 0) {
                    break;
                }
            }
            if (x >= 0) {
                items[x][y] = info;
                info.cellX = x;
                info.cellY = y;
                info.screenId = screenId;
                return true;
            }
            return false;
        }

        public boolean isFulled() {
            for (int i = 0; i < items.length; i++) {
                ItemInfo[] item = items[i];
                if (item == null) {
                    return false;
                }
                for (int j = 0; j < item.length; j++) {
                    if (item[j] == null) {
                        return false;
                    }
                }
            }
            return true;
        }

    }
}
